package parser;

import error.Error;
import error.ErrorType;
import lexer.LexType;
import lexer.Token;
import llvmir.value.structure.Function;
import node.*;
import symbol.*;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class Parser {
    public static BufferedWriter parserWriter;
    private static Parser instance; // 单例模式
    private List<Token> tokenList; // 词法分析结果,直接拿过来

    private Token currentToken; // 当前token
    private Token nextToken; // 下一个token
    private int index = 0; // tokenList的索引

    private ScopeStack scopeStack = ScopeStack.getInstance();
    private Error error = Error.getInstance();
    private SymbolBuilder symbolBuilder = SymbolBuilder.getInstance();
    private IrBuilder irBuilder = IrBuilder.getInstance();

    private Parser() {
    }

    public static Parser getInstance() {
        if (instance == null) {
            instance = new Parser();
        }
        return instance;
    }

    public void initBuffer(String outputFile) throws IOException {
        parserWriter = new BufferedWriter(new FileWriter(outputFile));
    }

    public void closeBuffer() throws IOException {
        parserWriter.close();
    }

    public void getToken() {
        if (index < tokenList.size()) {
            currentToken = tokenList.get(index);
            if (index + 1 < tokenList.size()) {
                nextToken = tokenList.get(index + 1);
            } else {
                nextToken = null;
            }
            index++;
        } else {
            currentToken = null;
            nextToken = null;
        }
    }

    public void retract() {
        index = Math.max(0, index - 2);
        getToken();
    }

    public List<Token> getTokenList() {
        return tokenList;
    }

    public void setTokenList(List<Token> tokenList) {
        this.tokenList = tokenList;
    }

    public void setScopeStack(ScopeStack scopeStack) {
        this.scopeStack = scopeStack;
    }

    public boolean isUnaryOperator(LexType type) {
        return type == LexType.PLUS || type == LexType.MINU || type == LexType.NOT;
    }

    public CompUnitNode parseCompUnit() {
        // CompUnit → {Decl} {FuncDef} MainFuncDef
        List<DeclNode> declNodes = new ArrayList<>();
        List<FuncDefNode> funcDefNodes = new ArrayList<>();
        MainFuncDefNode mainFuncDefNode = null;

        scopeStack.enterScope();

        getToken();
        while (currentToken.getLexType() == LexType.CONSTTK
                || (currentToken.getLexType() == LexType.INTTK && nextToken.getLexType() != LexType.MAINTK
                        && tokenList.get(index + 1).getLexType() != LexType.LPARENT)
                || (currentToken.getLexType() == LexType.CHARTK
                        && tokenList.get(index + 1).getLexType() != LexType.LPARENT)) {
            DeclNode declNode = parseDecl();

            if (declNode != null) {
                declNodes.add(declNode);
            }
        }
        irBuilder.inGlobal = false;
        while ((currentToken.getLexType() == LexType.INTTK && nextToken.getLexType() != LexType.MAINTK)
                || currentToken.getLexType() == LexType.CHARTK
                || currentToken.getLexType() == LexType.VOIDTK) {
            FuncDefNode funcDefNode = parseFuncDef();
            if (funcDefNode != null) {
                funcDefNodes.add(funcDefNode);
            }
        }
        if (currentToken.getLexType() == LexType.INTTK && nextToken.getLexType() == LexType.MAINTK) {
            mainFuncDefNode = parseMainFuncDef();
        }
        CompUnitNode compUnitNode = new CompUnitNode(declNodes, funcDefNodes, mainFuncDefNode);

        scopeStack.exitScope();

        return compUnitNode;
    }

    public DeclNode parseDecl() {
        // Decl → ConstDecl | VarDecl
        ConstDeclNode constDeclNode;
        VarDeclNode varDeclNode;

        if (currentToken.getLexType() == LexType.CONSTTK) {
            constDeclNode = parseConstDecl();
            return new DeclNode(constDeclNode);
        } else if (currentToken.getLexType() == LexType.INTTK || currentToken.getLexType() == LexType.CHARTK) {
            varDeclNode = parseVarDecl();
            return new DeclNode(varDeclNode);
        } else {
            System.err.println("Decl error");
            return null;
        }
    }

    private BTypeNode parseBType() {
        if (currentToken.getLexType() == LexType.INTTK) {
            return new BTypeNode("int");
        } else if (currentToken.getLexType() == LexType.CHARTK) {
            return new BTypeNode("char");
        } else {
            System.err.println("BType error");
            return null;
        }
    }

    public ConstDeclNode parseConstDecl() {
        // ConstDecl → 'const' BType ConstDef { ',' ConstDef } ';'
        BTypeNode bTypeNode;
        List<ConstDefNode> constDefNodes = new ArrayList<>();

        getToken();
        // parseBType任务在这里完成
        bTypeNode = parseBType();
        getToken();

        ConstDefNode constDefNode = parseConstDef();

        if (constDefNode != null) {
            {
                constDefNodes.add(constDefNode);
                symbolBuilder.buildConst(bTypeNode, constDefNode);
            }
        }
        while (currentToken.getLexType() == LexType.COMMA) {
            getToken();
            constDefNode = parseConstDef();
            if (constDefNode != null) {
                constDefNodes.add(constDefNode);
                symbolBuilder.buildConst(bTypeNode, constDefNode);
            }
        }
        if (currentToken.getLexType() == LexType.SEMICN) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.I);
        }

        ConstDeclNode constDeclNode = new ConstDeclNode(bTypeNode, constDefNodes);
        if (irBuilder.inGlobal && !error.hasError)
            irBuilder.buildGlobalVariable(constDeclNode);
        return constDeclNode;
    }

    public ConstDefNode parseConstDef() {
        // ConstDef → Ident [ '[' ConstExp ']' ] '=' ConstInitVal
        String identNode;
        ConstExpNode constExpNode = null;
        ConstInitValNode constInitValNode;
        int lineno;

        if (currentToken.getLexType() == LexType.IDENFR) {
            identNode = currentToken.getContent();
            lineno = currentToken.getLineno();
            getToken();
        } else {
            System.err.println("ConstDef error");
            return null;
        }
        if (currentToken.getLexType() == LexType.LBRACK) {
            getToken();
            constExpNode = parseConstExp();
            if (currentToken.getLexType() == LexType.RBRACK) {
                getToken();
            } else {
                error.addError(tokenList.get(index - 2).getLineno(), ErrorType.K);
            }
        }
        if (currentToken.getLexType() == LexType.ASSIGN) {
            getToken();
        } else {
            System.err.println("ConstDef error");
            return null;
        }
        constInitValNode = parseConstInitVal();
        ConstDefNode constDefNode = new ConstDefNode(identNode, constExpNode, constInitValNode);
        constDefNode.setLineno(lineno);
        return constDefNode;
    }

    public ConstInitValNode parseConstInitVal() {
        // ConstInitVal → ConstExp | '{' [ ConstExp { ',' ConstExp } ] '}' | StringConst
        ConstExpNode constExpNode;
        List<ConstExpNode> constExpNodes = new ArrayList<>();
        String stringConst;

        if (currentToken.getLexType() == LexType.LBRACE && nextToken.getLexType() != LexType.RBRACE) {
            getToken();
            constExpNode = parseConstExp();
            if (constExpNode != null) {
                constExpNodes.add(constExpNode);
            }
            while (currentToken.getLexType() == LexType.COMMA) {
                getToken();
                constExpNode = parseConstExp();
                if (constExpNode != null) {
                    constExpNodes.add(constExpNode);
                }
            }
            if (currentToken.getLexType() == LexType.RBRACE) {
                getToken();
            } else {
                System.err.println("ConstInitVal error");
                return null;
            }
            return new ConstInitValNode(constExpNodes);
        } else if (currentToken.getLexType() == LexType.LBRACE && nextToken.getLexType() == LexType.RBRACE) {
            getToken();
            getToken();
            return new ConstInitValNode(constExpNodes);
        } else if (currentToken.getLexType() == LexType.STRCON) {
            stringConst = currentToken.getContent();
            getToken();
            return new ConstInitValNode(stringConst);
        } else {
            constExpNode = parseConstExp();
            if (constExpNode != null) {
                return new ConstInitValNode(constExpNode);
            } else {
                System.err.println("ConstInitVal error");
                return null;
            }
        }
    }

    public VarDeclNode parseVarDecl() {
        // VarDecl → BType VarDef { ',' VarDef } ';'
        BTypeNode bTypeNode;
        List<VarDefNode> varDefNodes = new ArrayList<>();

        // parseBType任务在这里完成
        bTypeNode = parseBType();
        getToken();

        VarDefNode varDefNode = parseVarDef();
        if (varDefNode != null) {
            varDefNodes.add(varDefNode);
            symbolBuilder.buildVar(bTypeNode, varDefNode);
        }
        while (currentToken.getLexType() == LexType.COMMA) {
            getToken();
            varDefNode = parseVarDef();
            if (varDefNode != null) {
                varDefNodes.add(varDefNode);
                symbolBuilder.buildVar(bTypeNode, varDefNode);
            }
        }
        if (currentToken.getLexType() == LexType.SEMICN) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.I);
        }
        VarDeclNode varDeclNode = new VarDeclNode(bTypeNode, varDefNodes);
        if (irBuilder.inGlobal && !error.hasError)
            irBuilder.buildGlobalVariable(varDeclNode);

        return varDeclNode;
    }

    public VarDefNode parseVarDef() {
        // VarDef → Ident [ '[' ConstExp ']' ] | Ident [ '[' ConstExp ']' ] '=' InitVal
        String identNode;
        ConstExpNode constExpNode = null;
        InitValNode initValNode;
        int lineno;

        if (currentToken.getLexType() == LexType.IDENFR) {
            identNode = currentToken.getContent();
            lineno = currentToken.getLineno();
            getToken();
        } else {
            System.err.println("VarDef error");
            return null;
        }
        if (currentToken.getLexType() == LexType.LBRACK) {
            getToken();
            constExpNode = parseConstExp();
            if (currentToken.getLexType() == LexType.RBRACK) {
                getToken();
            } else {
                error.addError(tokenList.get(index - 2).getLineno(), ErrorType.K);
            }
        }
        if (currentToken.getLexType() == LexType.ASSIGN) {
            getToken();
            initValNode = parseInitVal();

            VarDefNode varDefNode = new VarDefNode(identNode, constExpNode, initValNode);
            varDefNode.setLineno(lineno);
            return varDefNode;
        } else {
            VarDefNode varDefNode = new VarDefNode(identNode, constExpNode);
            varDefNode.setLineno(lineno);
            return varDefNode;
        }
    }

    public InitValNode parseInitVal() {
        // InitVal → Exp | '{' [ Exp { ',' Exp } ] '}' | StringConst
        ExpNode expNode;
        List<ExpNode> expNodes = new ArrayList<>();
        String stringConst;

        if (currentToken.getLexType() == LexType.LBRACE && nextToken.getLexType() != LexType.RBRACE) {
            getToken();
            expNode = parseExp();
            if (expNode != null) {
                expNodes.add(expNode);
            }
            while (currentToken.getLexType() == LexType.COMMA) {
                getToken();
                expNode = parseExp();
                if (expNode != null) {
                    expNodes.add(expNode);
                }
            }
            if (currentToken.getLexType() == LexType.RBRACE) {
                getToken();
            } else {
                System.err.println("InitVal error");
                return null;
            }
            return new InitValNode(expNodes);
        } else if (currentToken.getLexType() == LexType.LBRACE && nextToken.getLexType() == LexType.RBRACE) {
            getToken();
            getToken();
            return new InitValNode(expNodes);
        } else if (currentToken.getLexType() == LexType.STRCON) {
            stringConst = currentToken.getContent();
            getToken();
            return new InitValNode(stringConst);
        } else {
            expNode = parseExp();
            if (expNode != null) {
                return new InitValNode(expNode);
            } else {
                System.err.println("InitVal error");
                return null;
            }
        }
    }

    public FuncTypeNode parseFuncType() {
        switch (currentToken.getLexType()) {
            case INTTK:
                error.inIntFunc = true;
                return new FuncTypeNode("int");
            case CHARTK:
                error.inCharFunc = true;
                return new FuncTypeNode("char");
            case VOIDTK:
                error.inVoidFunc = true;
                return new FuncTypeNode("void");
            default:
                System.err.println("FuncType error");
                return null;
        }
    }

    public FuncDefNode parseFuncDef() {
        // FuncDef → FuncType Ident '(' [FuncFParams] ')' Block
        FuncTypeNode funcTypeNode;
        String identNode;
        int lineno;
        FuncFParamsNode funcFParamsNode = null;
        BlockNode blockNode;

        // parseFuncType任务在这里完成
        funcTypeNode = parseFuncType();

        getToken();
        if (currentToken.getLexType() == LexType.IDENFR) {
            identNode = currentToken.getContent();
            lineno = currentToken.getLineno();
            getToken();
        } else {
            System.err.println("FuncDef error");
            return null;
        }

        if (currentToken.getLexType() == LexType.LPARENT) {
            getToken();
        } else {
            System.err.println("FuncDef error");
            return null;
        }

        if (currentToken.getLexType() == LexType.INTTK || currentToken.getLexType() == LexType.CHARTK) {
            funcFParamsNode = parseFuncFParams();
        }

        if (currentToken.getLexType() == LexType.RPARENT) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.J);
        }
        FuncDefNode funcDefNode = new FuncDefNode(funcTypeNode, identNode, funcFParamsNode);
        irBuilder.voidFuncHasReturn = true;
        symbolBuilder.buildFunc(funcTypeNode, identNode, funcFParamsNode, lineno);

        scopeStack.enterScope();

        if (funcFParamsNode != null)
            symbolBuilder.buildPara(funcFParamsNode);

        blockNode = parseBlockForFunc();

        boolean isReturn = false;
        // 判断函数Block中的最后一条语句是否为return语句
        if (!blockNode.getBlockItemNodes().isEmpty()) {
            BlockItemNode blockItemNode = blockNode.getBlockItemNodes()
                    .get(blockNode.getBlockItemNodes().size() - 1);
            if (blockItemNode.getStmtNode() != null) {
                StmtNode stmtNode = blockItemNode.getStmtNode();
                if (stmtNode.isReturn()) {
                    isReturn = true;
                }
            }
        }
        if (!isReturn) {
            // index-2是非main函数的左括号前的token
            if (error.inIntFunc || error.inCharFunc)
                error.addError(tokenList.get(index - 2).getLineno(), ErrorType.G);
            else if (error.inVoidFunc)
                irBuilder.voidFuncHasReturn = false;
        }

        error.inIntFunc = false;
        error.inCharFunc = false;
        error.inVoidFunc = false;

        funcDefNode = new FuncDefNode(funcTypeNode, identNode, funcFParamsNode, blockNode);

        Function function = null;
        if (!error.hasError)
            function = irBuilder.buildFunction(funcDefNode);

        scopeStack.exitScope();

        if (!error.hasError)
            irBuilder.buildFunctionBody(blockNode, function);

        return funcDefNode;
    }

    public MainFuncDefNode parseMainFuncDef() {
        // MainFuncDef → 'int' 'main' '(' ')' Block
        BlockNode blockNode;

        if (currentToken.getLexType() == LexType.INTTK) {
            getToken();
        } else {
            System.err.println("MainFuncDef error");
            return null;
        }

        if (currentToken.getLexType() == LexType.MAINTK) {
            getToken();
        } else {
            System.err.println("MainFuncDef error");
            return null;
        }

        if (currentToken.getLexType() == LexType.LPARENT) {
            getToken();
        } else {
            System.err.println("MainFuncDef error");
            return null;
        }

        if (currentToken.getLexType() == LexType.RPARENT) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.J);
        }
        error.inIntFunc = true;

        irBuilder.voidFuncHasReturn = true;

        scopeStack.enterScope();

        blockNode = parseBlockForFunc();
        boolean isReturn = false;
        // 判断函数Block中的最后一条语句是否为return语句
        if (!blockNode.getBlockItemNodes().isEmpty()) {
            BlockItemNode blockItemNode = blockNode.getBlockItemNodes().get(blockNode.getBlockItemNodes().size() - 1);
            if (blockItemNode.getStmtNode() != null) {
                StmtNode stmtNode = blockItemNode.getStmtNode();
                if (stmtNode.isReturn()) {
                    isReturn = true;
                }
            }
        }
        if (!isReturn) {
            // index-1是main函数的左括号前的token
            error.addError(tokenList.get(index - 1).getLineno(), ErrorType.G);
        }
        error.inIntFunc = false;

        Function function = null;
        if (!error.hasError)
            function = irBuilder.buildMainFunction();

        scopeStack.exitScope();

        if (!error.hasError)
            irBuilder.buildFunctionBody(blockNode, function);

        return new MainFuncDefNode(blockNode);
    }

    public FuncFParamsNode parseFuncFParams() {
        // FuncFParams → FuncFParam { ',' FuncFParam }
        List<FuncFParamNode> funcFParamNodes = new ArrayList<>();

        FuncFParamNode funcFParamNode = parseFuncFParam();
        if (funcFParamNode != null) {
            funcFParamNodes.add(funcFParamNode);
        }
        while (currentToken.getLexType() == LexType.COMMA) {
            getToken();
            funcFParamNode = parseFuncFParam();
            if (funcFParamNode != null) {
                funcFParamNodes.add(funcFParamNode);
            }
        }
        return new FuncFParamsNode(funcFParamNodes);
    }

    public FuncFParamNode parseFuncFParam() {
        // FuncFParam → BType Ident ['[' ']']
        BTypeNode bTypeNode;
        String identNode;
        int lineno;

        // parseBType任务在这里完成
        if (currentToken.getLexType() == LexType.INTTK) {
            bTypeNode = new BTypeNode("int");
        } else if (currentToken.getLexType() == LexType.CHARTK) {
            bTypeNode = new BTypeNode("char");
        } else {
            System.err.println("FuncFParam error");
            return null;
        }

        getToken();
        if (currentToken.getLexType() == LexType.IDENFR) {
            identNode = currentToken.getContent();
            lineno = currentToken.getLineno();
            getToken();
        } else {
            System.err.println("FuncFParam error");
            return null;
        }

        if (currentToken.getLexType() == LexType.LBRACK) {
            getToken();
            if (currentToken.getLexType() == LexType.RBRACK) {
                getToken();
            } else {
                error.addError(tokenList.get(index - 2).getLineno(), ErrorType.K);
            }
            // 这里实际上有问题，当出现一半左括号时，已经说明参数是数组，所以应该把return放在外面

            // 在错误B中，忘记传入lineno
            FuncFParamNode funcFParamNode = new FuncFParamNode(bTypeNode, identNode, true);
            funcFParamNode.setLineno(lineno);
            return funcFParamNode;
        }
        FuncFParamNode funcFParamNode = new FuncFParamNode(bTypeNode, identNode);
        funcFParamNode.setLineno(lineno);
        return funcFParamNode;
    }

    public BlockNode parseBlock() {
        // Block → '{' {BlockItem} '}'
        List<BlockItemNode> blockItemNodes = new ArrayList<>();

        scopeStack.enterScope();

        if (currentToken.getLexType() == LexType.LBRACE) {
            getToken();
        } else {
            System.err.println("Block error");
            return null;
        }

        while (currentToken.getLexType() != LexType.RBRACE) {
            BlockItemNode blockItemNode = parseBlockItem();
            if (blockItemNode != null) {
                blockItemNodes.add(blockItemNode);
            }
        }

        getToken();

        scopeStack.exitScope();

        return new BlockNode(blockItemNodes, scopeStack.getScopeNumberAfter());
    }

    public BlockNode parseBlockForFunc() {
        // Block → '{' {BlockItem} '}'
        List<BlockItemNode> blockItemNodes = new ArrayList<>();

        if (currentToken.getLexType() == LexType.LBRACE) {
            getToken();
        } else {
            System.err.println("Block error");
            return null;
        }

        while (currentToken.getLexType() != LexType.RBRACE) {
            BlockItemNode blockItemNode = parseBlockItem();
            if (blockItemNode != null) {
                blockItemNodes.add(blockItemNode);
            }
        }

        getToken();

        return new BlockNode(blockItemNodes, scopeStack.getScopeNumberBefore());
    }

    public BlockItemNode parseBlockItem() {
        // BlockItem → Decl | Stmt
        DeclNode declNode;
        StmtNode stmtNode;

        if (currentToken.getLexType() == LexType.CONSTTK || currentToken.getLexType() == LexType.INTTK
                || currentToken.getLexType() == LexType.CHARTK) {
            declNode = parseDecl();
            return new BlockItemNode(declNode);
        } else {
            stmtNode = parseStmt();
            return new BlockItemNode(stmtNode);
        }
    }

    public StmtNode parseStmt() {
        /*
         * Stmt → LVal '=' Exp ';'
         * | [Exp] ';'
         * | Block
         * | 'if' '(' Cond ')' Stmt [ 'else' Stmt ]
         * | 'for' '(' [ForStmt] ';' [Cond] ';' [ForStmt] ')' Stmt
         * | 'break' ';' | 'continue' ';'
         * | 'return' [Exp] ';'
         * | LVal '=' 'getint''('')'';'
         * | LVal '=' 'getchar''('')'';'
         * | 'printf''('StringConst {','Exp}')'';'
         */
        switch (currentToken.getLexType()) {
            case INTCON, CHRCON, LPARENT, SEMICN, PLUS, MINU, NOT:
                return parseExpressionStmt();
            case IDENFR:
                return parseAssignmentOrExpStmt();
            case LBRACE:
                return new StmtNode(parseBlock());
            case IFTK:
                return parseIfStmt();
            case FORTK:
                return parseForToStmt();
            case BREAKTK:
                return parseBreakStmt();
            case CONTINUETK:
                return parseContinueStmt();
            case RETURNTK:
                return parseReturnStmt();
            case PRINTFTK:
                return parsePrintfStmt();
            default:
                System.err.println("Stmt error");
                return null;
        }
    }

    private StmtNode parseExpressionStmt() {
        ExpNode expNode = null;
        // 不是分号而且能进来，一定是Exp的头符号集
        if (currentToken.getLexType() != LexType.SEMICN) {
            expNode = parseExp();
        }
        if (currentToken.getLexType() == LexType.SEMICN) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.I);
        }
        return new StmtNode(expNode);
    }

    private StmtNode parseAssignmentOrExpStmt() {
        int tempIndex = index;
        LValNode lValNode = parseLVal();

        if (currentToken.getLexType() == LexType.ASSIGN) {
            getToken();
            return parseAssignmentStmt(lValNode);
        } else {
            // 回退，解析为表达式
            index = tempIndex - 1;
            getToken();
            return parseExpressionStmt();
        }
    }

    private StmtNode parseAssignmentStmt(LValNode lValNode) {
        Symbol symbol = scopeStack.lookup(lValNode.getIdentNode());
        error.checkConstantAssignment(symbol, lValNode.getLineno());

        if (currentToken.getLexType() == LexType.GETINTTK || currentToken.getLexType() == LexType.GETCHARTK) {
            String funcType = currentToken.getLexType() == LexType.GETINTTK ? "getint" : "getchar";
            getToken();
            if (currentToken.getLexType() == LexType.LPARENT) {
                getToken();
            } else {
                System.err.println("Stmt error");
                return null;
            }
            if (currentToken.getLexType() == LexType.RPARENT) {
                getToken();
            } else {
                error.addError(tokenList.get(index - 2).getLineno(), ErrorType.J);
            }
            if (currentToken.getLexType() == LexType.SEMICN) {
                getToken();
            } else {
                error.addError(tokenList.get(index - 2).getLineno(), ErrorType.I);
            }
            return new StmtNode(lValNode, funcType);
        } else {
            ExpNode expNode = parseExp();
            if (currentToken.getLexType() == LexType.SEMICN) {
                getToken();
            } else {
                error.addError(tokenList.get(index - 2).getLineno(), ErrorType.I);
            }
            return new StmtNode(lValNode, expNode);
        }
    }

    private StmtNode parseForToStmt() {
        ForStmtNode forStmtNode1 = null;
        CondNode condNode = null;
        ForStmtNode forStmtNode2 = null;
        StmtNode stmtNode1 = null;

        getToken();
        if (currentToken.getLexType() == LexType.LPARENT) {
            getToken();
        } else {
            System.err.println("Stmt error");
            return null;
        }
        if (currentToken.getLexType() != LexType.SEMICN) {
            forStmtNode1 = parseForStmt();
        }
        if (currentToken.getLexType() == LexType.SEMICN) {
            getToken();
        } else {
            System.err.println("Stmt error");
            return null;
        }
        if (currentToken.getLexType() != LexType.SEMICN) {
            // [Cond]
            condNode = parseCond();
        }
        if (currentToken.getLexType() == LexType.SEMICN) {
            getToken();
        } else {
            System.err.println("Stmt error");
            return null;
        }
        // 这里之前对文法解析有问题
        if (currentToken.getLexType() != LexType.RPARENT) {
            forStmtNode2 = parseForStmt();
        }
        if (currentToken.getLexType() == LexType.RPARENT) {
            getToken();
        } else {
            System.err.println("Stmt error");
            return null;
        }
        error.inFor++;
        stmtNode1 = parseStmt();
        error.inFor--;
        return new StmtNode(forStmtNode1, condNode, forStmtNode2, stmtNode1);
    }

    private StmtNode parseBreakStmt() {
        if (!error.inFor()) {
            error.addError(currentToken.getLineno(), ErrorType.M);
        }
        getToken();
        if (currentToken.getLexType() == LexType.SEMICN) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.I);
        }
        return new StmtNode("break");
    }

    private StmtNode parseIfStmt() {
        CondNode condNode = null;
        StmtNode stmtNode1 = null;
        StmtNode stmtNode2 = null;

        getToken();
        if (currentToken.getLexType() == LexType.LPARENT) {
            getToken();
        } else {
            System.err.println("Stmt error");
            return null;
        }
        condNode = parseCond();
        if (currentToken.getLexType() == LexType.RPARENT) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.J);
        }
        stmtNode1 = parseStmt();
        if (currentToken.getLexType() == LexType.ELSETK) {
            getToken();
            stmtNode2 = parseStmt();
        }
        return new StmtNode(condNode, stmtNode1, stmtNode2);
    }

    private StmtNode parseContinueStmt() {
        if (!error.inFor()) {
            error.addError(currentToken.getLineno(), ErrorType.M);
        }
        getToken();
        if (currentToken.getLexType() == LexType.SEMICN) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.I);
        }
        return new StmtNode("continue");
    }

    private StmtNode parseReturnStmt() {
        ExpNode expNode = null;
        getToken();
        if (currentToken.getLexType() != LexType.SEMICN && ExpNode.contains(currentToken.getLexType())) {
            expNode = parseExp();
        }
        if (currentToken.getLexType() == LexType.SEMICN) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.I);
        }
        if (error.inVoidFunc && expNode != null) {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.F);
        }
        return new StmtNode(expNode, true);
    }

    private StmtNode parsePrintfStmt() {
        List<ExpNode> expNodeList = new ArrayList<>();
        ExpNode expNode;
        String stringConst;
        int lineno = currentToken.getLineno();
        getToken();
        if (currentToken.getLexType() == LexType.LPARENT) {
            getToken();
        } else {
            System.err.println("Stmt error");
            return null;
        }
        if (currentToken.getLexType() == LexType.STRCON) {
            stringConst = currentToken.getContent();
            getToken();
        } else {
            System.err.println("Stmt error");
            return null;
        }
        while (currentToken.getLexType() == LexType.COMMA) {
            getToken();
            expNode = parseExp();
            if (expNode != null) {
                expNodeList.add(expNode);
            }
        }
        // L类错误：printf中格式字符与表达式个数不匹配
        // 找到字符串中包含子串%c和%d的个数
        error.checkPrintfType(stringConst, expNodeList, lineno);

        // J类错误
        if (currentToken.getLexType() == LexType.RPARENT) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.J);
        }
        // I类错误
        if (currentToken.getLexType() == LexType.SEMICN) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.I);
        }
        return new StmtNode(stringConst, expNodeList);
    }

    public ForStmtNode parseForStmt() {
        // ForStmt → LVal '=' Exp
        LValNode lValNode;
        ExpNode expNode;

        lValNode = parseLVal();
        if (currentToken.getLexType() == LexType.ASSIGN) {
            getToken();
        } else {
            System.err.println("ForStmt error");
            return null;
        }

        // testfile16，前期忘记处理
        // H类错误：对常量赋值
        String ident = lValNode.getIdentNode();
        Symbol symbol = scopeStack.lookup(ident);
        error.checkConstantAssignment(symbol, lValNode.getLineno());

        expNode = parseExp();
        return new ForStmtNode(lValNode, expNode);
    }

    public ExpNode parseExp() {
        // Exp → AddExp
        return new ExpNode(parseAddExp());
    }

    public CondNode parseCond() {
        // Cond → LOrExp
        return new CondNode(parseLOrExp());
    }

    public LValNode parseLVal() {
        // LVal → Ident [ '[' Exp ']' ]
        String identNode;
        ExpNode expNode;
        int lineno;

        if (currentToken.getLexType() == LexType.IDENFR) {
            identNode = currentToken.getContent();
            lineno = currentToken.getLineno();
            // C类错误
            if (scopeStack.lookup(identNode) == null) {
                error.addError(currentToken.getLineno(), ErrorType.C);
            }
            getToken();
        } else {
            System.err.println("LVal error");
            return null;
        }
        if (currentToken.getLexType() == LexType.LBRACK) {
            getToken();
            expNode = parseExp();
            if (currentToken.getLexType() == LexType.RBRACK) {
                getToken();
            } else {
                error.addError(tokenList.get(index - 2).getLineno(), ErrorType.K);
            }
            LValNode lValNode = new LValNode(identNode, expNode);
            lValNode.setLineno(lineno);
            return lValNode;
        }
        LValNode lValNode = new LValNode(identNode);
        lValNode.setLineno(lineno);
        return lValNode;
    }

    public PrimaryExpNode parsePrimaryExp() {
        // PrimaryExp → '(' Exp ')' | LVal | Number | Character
        ExpNode expNode;
        LValNode lValNode;
        NumberNode numberNode;
        CharacterNode characterNode;

        if (currentToken.getLexType() == LexType.LPARENT) {
            getToken();
            expNode = parseExp();
            if (currentToken.getLexType() == LexType.RPARENT) {
                getToken();
            } else {
                error.addError(tokenList.get(index - 2).getLineno(), ErrorType.J);
            }
            return new PrimaryExpNode(expNode);
        } else if (currentToken.getLexType() == LexType.INTCON) {
            numberNode = new NumberNode(currentToken.getContent());
            getToken();
            return new PrimaryExpNode(numberNode);
        } else if (currentToken.getLexType() == LexType.CHRCON) {
            characterNode = new CharacterNode(currentToken.getContent());
            getToken();
            return new PrimaryExpNode(characterNode);
        } else {
            lValNode = parseLVal();
            return new PrimaryExpNode(lValNode);
        }
    }

    private UnaryOpNode parseUnaryOp() {
        switch (currentToken.getLexType()) {
            case PLUS:
                return new UnaryOpNode('+');
            case MINU:
                return new UnaryOpNode('-');
            case NOT:
                return new UnaryOpNode('!');
            default:
                System.err.println("UnaryOp error");
                return null;
        }
    }

    public UnaryExpNode parseUnaryExp() {
        // UnaryExp → PrimaryExp | Ident '(' [FuncRParams] ')' | UnaryOp UnaryExp
        if (currentToken.getLexType() == LexType.IDENFR && nextToken.getLexType() == LexType.LPARENT) {
            return parseFuncCallExp();
        } else if (!isUnaryOperator(currentToken.getLexType())) {
            return new UnaryExpNode(parsePrimaryExp());
        } else {
            UnaryOpNode unaryOpNode = parseUnaryOp();
            getToken();
            UnaryExpNode unaryExpNode = parseUnaryExp();
            return new UnaryExpNode(unaryOpNode, unaryExpNode);
        }
    }

    private UnaryExpNode parseFuncCallExp() {
        String identNode = currentToken.getContent();
        int lineno = currentToken.getLineno();
        FuncRParamsNode funcRParamsNode = null;

        // 检查函数是否在符号表中定义，否则报告C类错误
        if (scopeStack.lookup(identNode) == null) {
            error.addError(lineno, ErrorType.C);
        }

        getToken(); // 获取 '('
        if (currentToken.getLexType() == LexType.LPARENT) {
            getToken();
        } else {
            System.err.println("UnaryExp error");
            return null;
        }

        // 如果不是右括号，解析函数参数
        if (currentToken.getLexType() != LexType.RPARENT && ExpNode.contains(currentToken.getLexType())) {
            funcRParamsNode = parseFuncRParams();
            error.checkFuncParams(identNode, lineno, funcRParamsNode); // 检查参数匹配
        }

        if (currentToken.getLexType() == LexType.RPARENT) {
            getToken();
        } else {
            error.addError(tokenList.get(index - 2).getLineno(), ErrorType.J);
        }
        return new UnaryExpNode(identNode, funcRParamsNode);
    }

    public FuncRParamsNode parseFuncRParams() {
        // FuncRParams → Exp { ',' Exp }
        List<ExpNode> expNodes = new ArrayList<>();

        ExpNode expNode = parseExp();
        if (expNode != null) {
            expNodes.add(expNode);
        }
        while (currentToken.getLexType() == LexType.COMMA) {
            getToken();
            expNode = parseExp();
            if (expNode != null) {
                expNodes.add(expNode);
            }
        }
        return new FuncRParamsNode(expNodes);
    }

    public MulExpNode parseMulExp() {
        // MulExp → UnaryExp | MulExp ('\*' | '/' | '%') UnaryExp
        // 改写文法
        // MulExp → UnaryExp {('\*' | '/' | '%') UnaryExp}
        List<UnaryExpNode> unaryExpNodes = new ArrayList<>();
        List<String> opList = new ArrayList<>();

        UnaryExpNode unaryExpNode = parseUnaryExp();
        if (unaryExpNode != null) {
            unaryExpNodes.add(unaryExpNode);
        }
        while (currentToken.getLexType() == LexType.MULT || currentToken.getLexType() == LexType.DIV
                || currentToken.getLexType() == LexType.MOD) {
            opList.add(currentToken.getContent());
            getToken();
            unaryExpNode = parseUnaryExp();
            if (unaryExpNode != null) {
                unaryExpNodes.add(unaryExpNode);
            }
        }
        return new MulExpNode(unaryExpNodes, opList);
    }

    public AddExpNode parseAddExp() {
        // AddExp → MulExp | AddExp ('+' | '-') MulExp
        // 改写文法
        // AddExp → MulExp { ('+' | '-') MulExp}
        List<MulExpNode> mulExpNodes = new ArrayList<>();
        List<String> opList = new ArrayList<>();

        MulExpNode mulExpNode = parseMulExp();
        if (mulExpNode != null) {
            mulExpNodes.add(mulExpNode);
        }

        while (currentToken.getLexType() == LexType.PLUS || currentToken.getLexType() == LexType.MINU) {
            opList.add(currentToken.getContent());
            getToken();
            mulExpNode = parseMulExp();
            if (mulExpNode != null) {
                mulExpNodes.add(mulExpNode);
            }
        }
        return new AddExpNode(mulExpNodes, opList);
    }

    public RelExpNode parseRelExp() {
        // RelExp → AddExp | RelExp ('<' | '<=' | '>' | '>=') AddExp
        // 改写文法
        // RelExp → AddExp {('<' | '<=' | '>' | '>=') AddExp}
        List<AddExpNode> addExpNodes = new ArrayList<>();
        List<String> opList = new ArrayList<>();

        AddExpNode addExpNode = parseAddExp();
        if (addExpNode != null) {
            addExpNodes.add(addExpNode);
        }

        while (currentToken.getLexType() == LexType.LSS || currentToken.getLexType() == LexType.LEQ
                || currentToken.getLexType() == LexType.GRE || currentToken.getLexType() == LexType.GEQ) {
            opList.add(currentToken.getContent());
            getToken();
            addExpNode = parseAddExp();
            if (addExpNode != null) {
                addExpNodes.add(addExpNode);
            }
        }
        return new RelExpNode(addExpNodes, opList);
    }

    public EqExpNode parseEqExp() {
        // EqExp → RelExp | EqExp ('==' | '!=') RelExp
        // 改写文法
        // EqExp → RelExp {('==' | '!=') RelExp}
        List<RelExpNode> relExpNodes = new ArrayList<>();
        List<String> opList = new ArrayList<>();

        RelExpNode relExpNode = parseRelExp();
        if (relExpNode != null) {
            relExpNodes.add(relExpNode);
        }

        while (currentToken.getLexType() == LexType.EQL || currentToken.getLexType() == LexType.NEQ) {
            opList.add(currentToken.getContent());
            getToken();
            relExpNode = parseRelExp();
            if (relExpNode != null) {
                relExpNodes.add(relExpNode);
            }
        }

        return new EqExpNode(relExpNodes, opList);
    }

    public LAndExpNode parseLAndExp() {
        // LAndExp → EqExp | LAndExp '&&' EqExp
        // 改写文法
        // LAndExp → EqExp {'&&' EqExp}
        List<EqExpNode> eqExpNodes = new ArrayList<>();

        EqExpNode eqExpNode = parseEqExp();
        if (eqExpNode != null) {
            eqExpNodes.add(eqExpNode);
        }

        while (currentToken.getLexType() == LexType.AND) {
            getToken();
            eqExpNode = parseEqExp();
            if (eqExpNode != null) {
                eqExpNodes.add(eqExpNode);
            }
        }

        return new LAndExpNode(eqExpNodes);
    }

    public LOrExpNode parseLOrExp() {
        // LOrExp → LAndExp | LOrExp '||' LAndExp
        // 改写文法
        // LOrExp → LAndExp { '||' LAndExp }
        List<LAndExpNode> lAndExpNodes = new ArrayList<>();

        LAndExpNode lAndExpNode = parseLAndExp();
        if (lAndExpNode != null) {
            lAndExpNodes.add(lAndExpNode);
        }

        while (currentToken.getLexType() == LexType.OR) {
            getToken();
            lAndExpNode = parseLAndExp();
            if (lAndExpNode != null) {
                lAndExpNodes.add(lAndExpNode);
            }
        }

        return new LOrExpNode(lAndExpNodes);
    }

    public ConstExpNode parseConstExp() {
        // ConstExp → AddExp
        return new ConstExpNode(parseAddExp());
    }

    public void showParser() throws IOException {
        CompUnitNode compUnitNode = parseCompUnit();
        compUnitNode.show();
    }
}
